<template>
  <!--  连接-->
  <view style="margin: 1rem">
    <h1 style="text-align: center;padding-bottom: 0.5rem">Websocket</h1>
    <view>
      <uni-forms ref="form" :modelValue="connection" :rules="rules">
        <uni-forms-item label="Host" name="host">
          <uni-easyinput type="text" v-model="connection.host" />
        </uni-forms-item>
        <uni-forms-item label="Port" name="port">
          <uni-easyinput type="text" v-model="connection.port" />
        </uni-forms-item>
        <uni-forms-item label="Path" name="path">
          <uni-easyinput type="text" v-model="connection.path" />
        </uni-forms-item>
        <uni-forms-item label="Client ID" name="clientId">
          <uni-easyinput type="text" v-model="connection.clientId" />
        </uni-forms-item>
        <uni-forms-item label="Username" name="username">
          <uni-easyinput type="text" v-model="connection.username" />
        </uni-forms-item>
        <uni-forms-item label="PassWord" name="password">
          <uni-easyinput type="password" v-model="connection.password" />
        </uni-forms-item>
        <uni-forms-item label="Keep Alive" name="connectTimeout">
          <uni-easyinput type="text" v-model="connection.connectTimeout" />
        </uni-forms-item>
        <view>
          <checkbox-group>
            <label>
              <checkbox @click="connection.clean=!connection.clean" :checked="connection.clean" />Clean Session
            </label>
            <!--            <label>-->
            <!--              <checkbox value="cb" />SSL-->
            <!--            </label>-->
          </checkbox-group>
        </view>
      </uni-forms>
      <button type="primary" @click="Connect">Connect</button>
      <button type="warn" @click="Disconnect">Disconnect</button>
      <view>
        Current State: <span :class="[{currentStateTrue:connection.currentState==='CONNECTED'},{currentStateFalse:connection.currentState==='DISCONNECTED'}]">{{connection.currentState}}</span>
      </view>
    </view>
  </view>
  <hr>
  <!--  订阅-->
  <view style="margin: 1rem">
    <h1 style="text-align: center;padding-bottom: 0.5rem">Subscribe</h1>
    <view>
      <uni-forms ref="form" :modelValue="subscribe" :rules="rules">
        <uni-forms-item label="Topic" name="topic">
          <uni-easyinput type="text" v-model="subscribe.topic" />
        </uni-forms-item>
        <uni-section title="Qos" type="line">
          <uni-data-select
              v-model="subscribe.qos"
              :localdata="range"
          ></uni-data-select>
        </uni-section>
        <!--        <uni-forms-item label="Qos" name="qos">-->
        <!--          <uni-easyinput type="text" v-model="subscribe.qos" />-->
        <!--        </uni-forms-item>-->
      </uni-forms>
      <button type="primary" @click="Subscribe">Subscribe</button>
      <view>
        <!--        ======================================================================-->
        <view >
          <uni-table ref="table"  border stripe >
            <uni-tr>
              <uni-th width="90rpx" align="center">Topic</uni-th>
              <uni-th width="20rpx" align="center">qos</uni-th>
              <uni-th width="320rpx" align="center">Time</uni-th>
              <uni-th width="20rpx" align="center">操作</uni-th>
              <!--              <uni-th width="204" align="center">设置</uni-th>-->
            </uni-tr>
            <uni-tr v-for="(item, index) in subscribeList" :key="index">
              <uni-td>{{ item.topic }}</uni-td>
              <uni-td>
                <view >{{ item.qos }}</view>
              </uni-td>
              <uni-td>
                <view >{{ item.time }}</view>
              </uni-td>
              <uni-td>
                <view @click="unSubscribe(index,item.topic)">X</view>
              </uni-td>
            </uni-tr>
          </uni-table>

        </view>
      </view>
    </view>
  </view>
  <hr>
  <!--  发布-->
  <view style="margin: 1rem">
    <h1 style="text-align: center;padding-bottom: 0.5rem">Messages</h1>
    <view>
      <uni-forms ref="form" :modelValue="publishMsg" >
        <uni-forms-item label="Topic" name="topic">
          <uni-easyinput type="text" v-model="publishMsg.topic" />
        </uni-forms-item>
        <uni-forms-item label="Messages" name="messages">
          <uni-easyinput type="text" v-model="publishMsg.messages.msg" />
        </uni-forms-item>
        <uni-section title="Qos" type="line">
          <uni-data-select
              v-model="publishMsg.qos"
              :localdata="range"
          ></uni-data-select>
        </uni-section>
        <view>
          <checkbox-group>
            <label>
              <checkbox @click="publishMsg.retained=!publishMsg.retained" :checked="publishMsg.retained" />Retained
            </label>

          </checkbox-group>
        </view>
      </uni-forms>
      <button type="primary" @click="Messages">Send</button>
      <view>Messages already sent</view>
      <view>
        <uni-table ref="table"  border stripe >
          <uni-tr>
            <uni-th  align="center">Topic</uni-th>
            <uni-th  align="center">message</uni-th>
            <uni-th  align="center">qos</uni-th>
            <uni-th  align="center">time</uni-th>

          </uni-tr>
          <uni-tr v-for="(item, index) in alreadySent" :key="index">
            <uni-td>{{ item.topic }}</uni-td>
            <uni-td>
              <view>{{ item.messages }}</view>
            </uni-td>
            <uni-td>
              <view>{{ item.qos }}</view>
            </uni-td>
            <uni-td>
              <view>{{ item.time }}</view>
            </uni-td>
            <!--            <uni-td>-->
            <!--              <view @click="unSubscribe(index,item.topic)">X</view>-->
            <!--            </uni-td>-->
          </uni-tr>
        </uni-table>
        <!--        {{ receivedMessages}}-->
      </view>
      <view>Messages received</view>
      <view>
        <uni-table ref="table"  border stripe >
          <uni-tr>
            <uni-th  align="center">Topic</uni-th>
            <uni-th  align="center">message</uni-th>
            <uni-th  align="center">qos</uni-th>
            <uni-th  align="center">time</uni-th>
<!--            <uni-th  align="center">qos</uni-th>-->
<!--            <uni-th  align="center">操作</uni-th>-->
            <!--              <uni-th width="204" align="center">设置</uni-th>-->
          </uni-tr>
          <uni-tr v-for="(item, index) in receivedMessages" :key="index">
            <uni-td>{{ item.topic }}</uni-td>
            <uni-td>
              <view>{{ item.message.msg }}</view>
            </uni-td>
            <uni-td>
              <view>{{ item.qos.qos }}</view>
            </uni-td>
            <uni-td>
              <view>{{ item.time }}</view>
            </uni-td>
<!--            <uni-td>-->
<!--              <view @click="unSubscribe(index,item.topic)">X</view>-->
<!--            </uni-td>-->
          </uni-tr>
        </uni-table>
<!--        {{ receivedMessages}}-->
      </view>
    </view>
  </view>
</template>

<script setup>
import * as mqtt from 'mqtt/dist/mqtt.js';
import {
  reactive,
  ref,
  onMounted
} from "vue";

// https://github.com/mqttjs/MQTT.js#qos
let client = ref({
  connected: false
});
const receivedMessages = reactive([]);
const alreadySent=reactive([]);
const subscribedSuccess = ref(false);
const btnLoadingType = ref("");
const retryTimes = ref(0);

let protocal = "";
//如你的链接是wss:则修改为wxs:,如果你的链接是ws:则修改为wx:
// #ifdef H5
protocal = "ws"
// #endif
// #ifdef MP-WEIXIN
protocal = "wx"
// #endif
const connection = reactive({
  currentState:"DISCONNECTED",
  // ws or wss
  protocol: protocal,
  host: "jqrjq.cn",
  // ws -> 8083; wss -> 8084
  port: 8083,
  path:"mqtt",
  clientId: "emqx_vue3_" + Math.random().toString(16).substring(2, 8),
  /**
   * By default, EMQX allows clients to connect without authentication.
   * https://docs.emqx.com/en/enterprise/v4.4/advanced/auth.html#anonymous-login
   */
  username: "admin",
  password: "public",
  clean: true,
  connectTimeout: 30 * 1000, // ms
  reconnectPeriod: 4000, // ms
  // for more options and details, please refer to https://github.com/mqttjs/MQTT.js#mqttclientstreambuilder-options
});
const handleOnReConnect = () => {
  retryTimes.value += 1;
  if (retryTimes.value > 5) {
    try {
      client.value.end();
      initData();
      console.log("connection maxReconnectTimes limit, stop retry");
    } catch (error) {
      console.log("handleOnReConnect catch error:", error);
    }
  }
};
const createConnection = () => {
  try {
    const {
      protocol,
      host,
      port,
      path,
      ...options
    } = connection;
    const connectUrl = `${protocol}://${host}:${port}/${path}`;

    client.value = mqtt.connect(connectUrl, options);

    if (client.value.on) {
      // https://github.com/mqttjs/MQTT.js#event-connect
      client.value.on("connect", () => {
        btnLoadingType.value = "";
        console.log("connection successful");
        connection.currentState='CONNECTED';
      });

      // https://github.com/mqttjs/MQTT.js#event-reconnect
      client.value.on("reconnect", handleOnReConnect);

      // https://github.com/mqttjs/MQTT.js#event-error
      client.value.on("error", (error) => {
        console.log("connection error:", error);
      });
      //收到消息
      // https://github.com/mqttjs/MQTT.js#event-message
      client.value.on("message", (topic, message,qos) => {
        // receivedMessages.value = receivedMessages.value.concat(
        //     {message:message.toString(),topic:topic.toString()}
        // );
        receivedMessages.push( {message:JSON.parse(message.toString()),topic:topic.toString(),qos:qos,time:formattedDate(new Date())})
        console.log(`received message: ${message} from topic: ${topic}`);
      });
    }
  } catch (error) {
    console.log("mqtt.connect error:", error);
  }
};
/**
 * 订阅 subscribe
 * @constructor
 */
let subscribeList=reactive([]);
const range=reactive([
  { value: 0, text: '0' },//"最多交付一次"
  { value: 1, text: '1' },//"至少交付一次"
  { value: 2, text: '2' },//"至少交付一次"
])
const subscribe = reactive({
  topic:'testtopic/#',
  // ws or wss
  qos: 0,
});
const Subscribe=()=>{
  // client.subscribe('test', { qos: 0 }, function (error, granted) {
  client.value.subscribe(subscribe.topic,{qos:subscribe.qos}, () => {
    console.log(`Subscribe to topic '${subscribe.topic}'`)
    const exists = subscribeList.some(item =>
        item.topic === subscribe.topic && item.qos === subscribe.qos
    );
    if(!exists){
      subscribeList.push({'topic':subscribe.topic,'qos':subscribe.qos,time:formattedDate(new Date())})
    }

  })
}
const unSubscribe=(item,topic)=>{
  client.value.unsubscribe(topic, function (error) {
    if (error) {
      console.log(error)
    } else {
      subscribeList.splice(item,1);
      console.log('已取消订阅:',topic)
    }
  })
}
//发布
const publishMsg = reactive({
  topic:'testtopic',
  // messages: {
  //     "msg": "Hello, World!"
  // },
  qos: 0,
  retained:false,
  messages:{
    msg: "Hello, World!777"
  }
});

const Messages=()=>{
  alreadySent.push({topic:publishMsg.topic,
    messages:publishMsg.messages.msg,
    qos: publishMsg.qos ,
    time:formattedDate(new Date())
  })
  client.value.publish(publishMsg.topic, JSON.stringify(publishMsg.messages),{
        qos: publishMsg.qos ,
        retain: publishMsg.retain
      },  function (error){
        if (error) {
          console.error(error)
        }
      }
  )
}
//连接
const Connect=()=>{
  createConnection();
}
const Disconnect=()=>{
  console.log("关闭")
  client.clean=false;
  client.value.end();
  connection.currentState='DISCONNECTED';
}
const formattedDate=(date)=>{
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  const hours = String(date.getHours()).padStart(2, '0');
  const minutes = String(date.getMinutes()).padStart(2, '0');
  const seconds = String(date.getSeconds()).padStart(2, '0');
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
onMounted(() => {

})

</script>

<style >

.currentStateTrue{
  color: #4cd964;
}
.currentStateFalse{
  color: red;
}

</style>